package cn.walls1717.wallsmall.ware.config;

import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.*;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.amqp.support.converter.MessageConverter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.annotation.Resource;
import java.util.HashMap;

/**
 * RabbitMQ配置类
 *
 * @author walls1717
 * @since 2022/11/17 15:51
 **/
@Configuration
@Slf4j
public class RabbitConfig {
    @Resource
    private RabbitTemplate rabbitTemplate;

    /**
     * 配置完队列等信息，必须有监听者，才会自动创建。
     *
     * @param message message
     */
    /*@RabbitListener(queues = "stock.release.stock.queue")
    public void handle(Message message) {

    }*/

    /**
     * 延迟队列，检查订单状态，判断是否需要解锁库存。<br>
     * 到达指定时间，将消息重新发送给交换机，交换机将消息转发至普通队列，执行解锁库存操作。
     *
     * @return 死信队列
     */
    @Bean
    public Queue stockDelayQueue() {
        HashMap<String, Object> args = new HashMap<>();
        args.put("x-dead-letter-exchange", "stock-event-exchange");
        args.put("x-dead-letter-routing-key", "stock.release");
        args.put("x-message-ttl", 120000);
        return new Queue("stock.delay.queue", true, false, false, args);
    }

    /**
     * 普通队列，用于消费者消费。
     *
     * @return 普通队列
     */
    @Bean
    public Queue stockReleaseStockQueue() {
        return new Queue("stock.release.stock.queue", true, false, false);
    }

    @Bean
    public Exchange stockEventExchange() {
        return new TopicExchange("stock-event-exchange", true, false);
    }

    /**
     * 绑定关系<br>
     * stock.delay.queue <- stock.locked -> stock-event-exchange
     *
     * @return Binding
     */
    @Bean
    public Binding stockLockedBinding() {
        return new Binding("stock.delay.queue", Binding.DestinationType.QUEUE, "stock-event-exchange",
            "stock.locked", null);
    }

    /**
     * 绑定关系<br>
     * stock.release.stock.queue <- stock.release.# -> stock-event-exchange
     *
     * @return Binding
     */
    @Bean
    public Binding stockReleaseBinding() {
        return new Binding("stock.release.stock.queue", Binding.DestinationType.QUEUE, "stock-event-exchange",
            "stock.release.#", null);
    }

    /**
     * 使用Json序列化工具，对消息序列化。<br>
     * 默认使用的是JDK序列化工具
     *
     * @return MessageConverter
     */
    @Bean
    public MessageConverter messageConverter() {
        return new Jackson2JsonMessageConverter();
    }

    /**
     * 在RabbitConfig对象创建完成之后，执行此方法。<br>
     * RabbitTemplate初始化
     */
    // @PostConstruct
    public void initRabbitTemplate() {
        // 设置消息成功发送回调
        rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {
            /**
             * 确认方法<br>
             * 只要消息抵达broker，ack就为true。
             *
             *
             * @param correlationData 当前消息的唯一关联数据(消息唯一id)
             * @param ack 消息是否成功收到
             * @param cause 失败原因
             */
            @Override
            public void confirm(CorrelationData correlationData, boolean ack, String cause) {
                log.info("confirm -> {\"correlationData\":\"{}\",\"ack\":\"{}\",\"cause\":\"{}\"}", correlationData,
                    ack, cause);
            }
        });

        // 设置消息抵达队列回调
        rabbitTemplate.setReturnCallback(new RabbitTemplate.ReturnCallback() {
            /**
             * 消息没有投递给指定的队列，触发此失败回调。
             *
             * @param message 失败消息的详情
             * @param replyCode 回复状态码
             * @param replyText 回复消息
             * @param exchange 消息发动的交换机
             * @param routingKey 消息的路由键
             */
            @Override
            public void returnedMessage(Message message, int replyCode, String replyText, String exchange,
                String routingKey) {

                log.info("message -> {}", message);
                log.info(
                    "badReturn -> {\"replyCode\":\"{}\",\"replyText\":\"{}\",\"exchange\":\"{}\",\"routingKey\":\"{}\"}",
                    replyCode, replyText, exchange, routingKey);
            }
        });
    }
}
